/**************************************************************************
Step 1. Include necessary header files such that the stuff your test logic needs is declared. 
Don't forget gtest.h, which declares the testing framework.                                                   
**************************************************************************/
#include "gtest/gtest.h"
#include "include/pf_base64.h"
#include <include/pf_errno.h>


/**************************************************************************
Step 2. Use test fixture to design your complex cases. 
You can add SetUp() to initialize the test data and 
TearDown() to clear the test data for common usage
Pls remember that when you using test fixture, need to use macro "TEST_F" instead of "TEST"                                                 
**************************************************************************/
class PfBase64Test : public testing::Test 
{
    /* This SetUp() will be called before every TEST CASE which is marked by macro "TEST_F" */
    virtual void SetUp() 
    {

    }

    /* This TearDown will be called after every TEST CASE which is marked by macro "TEST_F" */
    virtual void TearDown() 
    {

    }

};

/***************************************************************************
Step 3. Use macro "TEST_F" to start your test case                                      
You can design your test case as the example
***************************************************************************/
/* test case for:
 * pf_base64_encode() : branch for input_size % 3 == 0 
 */
TEST_F(PfBase64Test, NormalTest1)
{
    /* Define the local variables */
    int ret = -1;
    const char* src_str = "weryo989azyouernezyoermzyweryo@#46292342342z,cwgiwyeTIywerbn"; 
    const char* last_2 = "/+";
    const char* expected_encoded_str = "d2VyeW85ODlhenlvdWVybmV6eW9lcm1" \
                                       "6eXdlcnlvQCM0NjI5MjM0MjM0MnosY3dnaXd5ZVRJeXdlcmJu";
    int32_t src_len = 0;
    int32_t desc_len = 0;
    char encoded_str[256] = {0};
    char decoded_str[256] = {0};

    /* initilize test data for this case */
    src_len = strlen(src_str);
    /* Call your function for test */
    desc_len = pf_base64_get_len(src_len, PF_BASE64_ENCODE);
    EXPECT_LE((int)strlen(expected_encoded_str), desc_len);
    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2);
    EXPECT_EQ(0, ret);
    EXPECT_STREQ(expected_encoded_str,encoded_str);

    desc_len = pf_base64_get_len(strlen(encoded_str), PF_BASE64_DECODE);
    EXPECT_LE((int)strlen(src_str), desc_len);
    ret = pf_base64_decode(encoded_str, strlen(encoded_str), decoded_str, desc_len + 1, last_2);
    EXPECT_EQ(0, ret);
    EXPECT_STREQ(src_str, decoded_str);

    /* Check the result */
    /* Clear the test data for this case */
}

/* test case for:
 * pf_base64_encode() : branch for input_size % 3 == 1 
 */
TEST_F(PfBase64Test, NormalTest2)
{
    /* Define the local variables */
    int ret = -1;
    const char* src_str = "e";
    const char* last_2 = "/+";
    const char* expected_encoded_str = "ZQ==";
    int32_t src_len = 0;
    int32_t desc_len = 0;
    char encoded_str[256] = {0};
    char decoded_str[256] = {0};

    /* initilize test data for this case */
    src_len = strlen(src_str);
    /* Call your function for test */
    desc_len = pf_base64_get_len(src_len, PF_BASE64_ENCODE);
    EXPECT_LE((int)strlen(expected_encoded_str), desc_len);
    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2);
    EXPECT_EQ(0, ret);
    EXPECT_STREQ(expected_encoded_str,encoded_str);

    desc_len = pf_base64_get_len(strlen(encoded_str), PF_BASE64_DECODE);
    EXPECT_LE((int)strlen(src_str), desc_len);
    ret = pf_base64_decode(encoded_str, strlen(encoded_str), decoded_str, desc_len + 1, last_2);
    EXPECT_EQ(0, ret);
    EXPECT_STREQ(src_str, decoded_str);

    /* Check the result */
    /* Clear the test data for this case */
}

/* test case for:
 * pf_base64_encode() : branch for input_size % 3 == 2 
 */
TEST_F(PfBase64Test, NormalTest3)
{
    /* Define the local variables */
    int ret = -1;
    const char* src_str = "uc";
    const char* last_2 = "/+";
    const char* expected_encoded_str = "dWM=";
    int32_t src_len = 0;
    int32_t desc_len = 0;
    char encoded_str[256] = {0};
    char decoded_str[256] = {0};

    /* initilize test data for this case */
    src_len = strlen(src_str);
    /* Call your function for test */
    desc_len = pf_base64_get_len(src_len, PF_BASE64_ENCODE);
    EXPECT_LE((int)strlen(expected_encoded_str), desc_len);
    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2);
    EXPECT_EQ(0, ret);
    EXPECT_STREQ(expected_encoded_str,encoded_str);

    desc_len = pf_base64_get_len(strlen(encoded_str), PF_BASE64_DECODE);
    EXPECT_LE((int)strlen(src_str), desc_len);
    ret = pf_base64_decode(encoded_str, strlen(encoded_str), decoded_str, desc_len + 1, last_2);
    EXPECT_EQ(0, ret);
    EXPECT_STREQ(src_str, decoded_str);

    /* Check the result */
    /* Clear the test data for this case */
}

/* test case for:
 * pf_base64_encode() : input_size == 0
 * pf_base64_decode() : input_size == 0
 */
TEST_F(PfBase64Test, ZeroTest)
{
    /* Define the local variables */
    int ret = -1;
    const char* src_str = "";
    const char* last_2 = "/+";
    const char* expected_encoded_str = "";
    int32_t src_len = 0;
    int32_t desc_len = 0;
    char encoded_str[256] = {0};
    char decoded_str[256] = {0};

    /* initilize test data for this case */
    src_len = strlen(src_str);
    /* Call your function for test */
    desc_len = pf_base64_get_len(src_len, PF_BASE64_ENCODE);
    EXPECT_LE((int)strlen(expected_encoded_str), desc_len);
    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2);
    EXPECT_EQ(0, ret);
    EXPECT_STREQ(expected_encoded_str,encoded_str);

    desc_len = pf_base64_get_len(strlen(encoded_str), PF_BASE64_DECODE);
    EXPECT_LE((int)strlen(src_str), desc_len);
    ret = pf_base64_decode(encoded_str, strlen(encoded_str), decoded_str, desc_len + 1, last_2);
    EXPECT_EQ(0, ret);
    EXPECT_STREQ(src_str, decoded_str);

    /* Check the result */
    /* Clear the test data for this case */
} 

/* test case for:
 * pf_check_last_2(): strlen(last_2) != 2
 *                    last_2[0] == last_2[1]
 *                    last_2[0] < '!' || last_2[0] > '~'
 *                    last_2[0] < '!' || last_2[0] > '~'
 *                    (last_2[0] >= '0' && last_2[0] <= '9') || 
 *                        (last_2[0] >= 'A' && last_2[0] <= 'Z') || 
 *                        (last_2[0] >= 'a' && last_2[0] <= 'z') || 
 *                        (last_2[0] == '=')
 *                    (last_2[1] >= '0' && last_2[1] <= '9') || 
 *                        (last_2[1] >= 'A' && last_2[1] <= 'Z') || 
 *                        (last_2[1] >= 'a' && last_2[1] <= 'z') || 
 *                        (last_2[1] == '=')
 *
 */
TEST_F(PfBase64Test, Last2Test)
{
    /* Define the local variables */
    int ret                           = -1;
    const char* src_str               = "zyhou weyob9238273472&2@C.Y";
    const char* last_2_1              = "";                    /* test case for strlen(last_2) != 2 */
    const char* last_2_2              = "//";                  /* test case for last_2[0] == last_2[1] */
    const char* last_2_3              = "\r+";                 /* test case for last_2[0] < '!'  */
    char last_2_4[]                   = {'~' + 1, '+', '\0'};  /* test case for last_2[0] > '~'  */
    char last_2_5[]                   = {'/', '\n', '\0'};     /* test case for last_2[1] < '!'  */
    char last_2_6[]                   = {'/', '~' + 1, '\0'};  /* test case for last_2[1] > '~'  */
    const char* last_2_7              = "O+";                  /* test case for last_2[0] in [A, Z] */
    const char* last_2_8              = "z+";                  /* test case for last_2[0] in [a, z] */
    const char* last_2_9              = "3+";                  /* test case for last_2[0] in [0, 9] */
    const char* last_2_10             = "=+";                  /* test case for last_2[0] == '=' */
    const char* last_2_11             = "/A";                  /* test case for last_2[1] in [A, Z] */
    const char* last_2_12             = "/i";                  /* test case for last_2[1] in [a, z] */
    const char* last_2_13             = "/0";                  /* test case for last_2[1] in [0, 9] */
    const char* last_2_14             = "/=";                  /* test case for last_2[1] == '=' */
    const char* expected_encoded_str  = "enlob3Ugd2V5b2I5MjM4MjczNDcyJjJAQy5Z";
    int32_t src_len                   = 0;
    int32_t desc_len                  = 0;
    char encoded_str[256]             = {0};
    char decoded_str[256]             = {0};

    /* initilize test data for this case */
    src_len = strlen(src_str);
    /* Call your function for test */
    desc_len = pf_base64_get_len(src_len, PF_BASE64_ENCODE);
    EXPECT_LE((int)strlen(expected_encoded_str), desc_len);

    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2_1);
    EXPECT_EQ(PF_EINVAL, ret);

    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2_2);
    EXPECT_EQ(PF_EINVAL, ret);

    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2_3);
    EXPECT_EQ(PF_EINVAL, ret);

    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2_4);
    EXPECT_EQ(PF_EINVAL, ret);

    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2_5);
    EXPECT_EQ(PF_EINVAL, ret);

    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2_6);
    EXPECT_EQ(PF_EINVAL, ret);

    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2_7);
    EXPECT_EQ(PF_EINVAL, ret);

    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2_8);
    EXPECT_EQ(PF_EINVAL, ret);

    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2_9);
    EXPECT_EQ(PF_EINVAL, ret);

    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2_10);
    EXPECT_EQ(PF_EINVAL, ret);

    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2_10);
    EXPECT_EQ(PF_EINVAL, ret);

    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2_10);
    EXPECT_EQ(PF_EINVAL, ret);

    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2_11);
    EXPECT_EQ(PF_EINVAL, ret);

    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2_12);
    EXPECT_EQ(PF_EINVAL, ret);

    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2_13);
    EXPECT_EQ(PF_EINVAL, ret);

    ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, last_2_14);
    EXPECT_EQ(PF_EINVAL, ret);

    desc_len = pf_base64_get_len(strlen(expected_encoded_str), PF_BASE64_DECODE);
    EXPECT_LE((int)strlen(src_str), desc_len);

    ret = pf_base64_decode(expected_encoded_str, strlen(expected_encoded_str), decoded_str, desc_len + 1, last_2_1);
    EXPECT_EQ(PF_EINVAL, ret);

    /* Check the result */
    /* Clear the test data for this case */
}

#ifndef VALGRIND_CHECK /* Death test core dump makes valgrind too many errors */
/* test case for:
 * pf_base64_get_len(): input_len < 0
 *                      type invalid
 * pf_base64_encode() : input == NULL
 *                      input_len < 0
 *                      output == NULL 
 *                      output_len < 0
 *                      last_2 == NULL
 *                      output_len < input_len
 * pf_base64_decode() : input == NULL
 *                      input_len < 0
 *                      output == NULL
 *                      output_len < 0
 *                      last_2 == NULL
 *
 */
TEST_F(PfBase64Test, DeathTest)
{
    ::testing::FLAGS_gtest_death_test_style = "threadsafe"; /* avoid death warning */

    /* Define the local variables */
    int ret = -1;
    const char* src_str = "world, hi";
    const char* last_2 = "/+";
    const char* expected_encoded_str = "d29ybGQsIGhp";
    int32_t src_len = 0;
    int32_t desc_len = 0;
    char encoded_str[256] = {0};
    char decoded_str[256] = {0};

    /* initilize test data for this case */
    src_len = strlen(src_str);
    /* Call your function for test */
    /* Check the result */
    EXPECT_DEBUG_DEATH((desc_len = pf_base64_get_len(-1, PF_BASE64_ENCODE)), "");

    desc_len = pf_base64_get_len(src_len, (PF_BASE64_CALC_TYPE)10);
    EXPECT_EQ(PF_EINVAL, desc_len);

    desc_len = pf_base64_get_len(src_len, PF_BASE64_ENCODE);

    EXPECT_DEBUG_DEATH((ret = pf_base64_encode(NULL, src_len, encoded_str, desc_len + 1, last_2)),
                                                 "");
    EXPECT_DEBUG_DEATH((ret = pf_base64_encode(src_str, -1, encoded_str, desc_len + 1, last_2)), "");
    EXPECT_DEBUG_DEATH((ret = pf_base64_encode(src_str, src_len, NULL, desc_len + 1, last_2)), "");
    EXPECT_DEBUG_DEATH((ret = pf_base64_encode(src_str, src_len, encoded_str, -1, last_2)), "");
    EXPECT_DEBUG_DEATH((ret = pf_base64_encode(src_str, src_len, encoded_str, desc_len + 1, NULL)),
                                                 "");

    ret = pf_base64_encode(src_str, src_len, encoded_str, 1, last_2);
    EXPECT_EQ(PF_EINVAL, ret);

    desc_len = pf_base64_get_len(strlen(expected_encoded_str), PF_BASE64_DECODE);
    EXPECT_LE((int)strlen(src_str), desc_len);
    EXPECT_DEBUG_DEATH((ret = pf_base64_decode(NULL, strlen(expected_encoded_str), 
                                                decoded_str, desc_len + 1, last_2)), "");
    EXPECT_DEBUG_DEATH((ret = pf_base64_decode(expected_encoded_str, -10, 
                                                decoded_str, desc_len + 1, last_2)), "");
    EXPECT_DEBUG_DEATH((ret = pf_base64_decode(expected_encoded_str, strlen(expected_encoded_str), 
                                                  NULL, desc_len + 1, last_2)), "");
    EXPECT_DEBUG_DEATH((ret = pf_base64_decode(expected_encoded_str, strlen(expected_encoded_str), 
                                                  decoded_str, -100, last_2)), "");
    EXPECT_DEBUG_DEATH((ret = pf_base64_decode(expected_encoded_str, strlen(expected_encoded_str), 
                                                  decoded_str, desc_len + 1, NULL)), "");

    ret = pf_base64_decode(expected_encoded_str, strlen(expected_encoded_str), decoded_str, 1, last_2);
    EXPECT_EQ(PF_EINVAL, ret);

    /* Clear the test data for this case */
}
#endif /* #ifndef VALGRIND_CHECK */
